/**
 * @description 初始化three
 * @params {dom} canvas
 * @params {Object} options
 *
 */
import * as THREE from 'three'
import { Scene, Mesh } from 'three'
import CannonWorld from './base/CannonWorld'
import Camera from './base/Camera'
import Renderer from './base/Renderer'
import Lights from './base/Lights'
import Size from './base/Size'
import Debug from './base/Debug'
import Time from './base/Time'
import World from './world/index'
import Resource from './base/Resource'
import ModalSource from './source/modal'

const defaultOptions = {
  cameraPosition: [500, 500, 500], // 初始化相机位置
  debug: true, // 是否开启调试 坐标系/gui/栅格
}
export default class Base3D {
  static instance
  constructor(canvas, options) {
    if (Base3D.instance) {
      return Base3D.instance
    }
    Base3D.instance = this
    this.scene
    this.canvas = canvas
    this.options = Object.assign(defaultOptions, options)
    this.camera
    this.renderer
    this.lights
    this.size
    this.time
    this.debug
    this.resource
    this.animationFrameId
    this.world
    this.cannonWorld
    this.init()
  }
  static getInstance() {
    return Base3D.instance
  }
  init() {
    this.size = new Size()
    this.time = new Time()
    this.scene = new THREE.Scene()
    // this.scene.fog = new THREE.FogExp2(0x03ccbb, 0.2)
    this.resource = new Resource(ModalSource)
    this.camera = new Camera()
    this.camera.instance.position.set(...this.options.cameraPosition)
    this.renderer = new Renderer()
    this.lights = new Lights()
    this.debug = new Debug()
    this.world = new World()
    this.cannonWorld = new CannonWorld()
    this.setListeners()
  }
  setListeners() {
    window.addEventListener('resize', this.resize.bind(this))
    this.animationFrameId = window.requestAnimationFrame(this.update.bind(this))
  }
  resize() {
    this.size.resize()
    this.camera.resize()
    this.renderer.resize()
    this.world.resize()
  }
  update() {
    this.time.update()
    this.camera.update()
    this.world.update()
    this.cannonWorld.update()
    this.renderer.update()
    this.debug.stats.update()
    this.animationFrameId = window.requestAnimationFrame(this.update.bind(this))
  }
  removeListners() {
    window.cancelAnimationFrame(this.animationFrameId)
    window.removeEventListener('resize', this.resize.bind(this))
  }
  destroy() {
    this.removeListners()
    this.scene.traverse((child) => {
      if (child instanceof Mesh) {
        child.geometry.dispose()
        for (const key in child.material) {
          const material = child.material[key]
          material.dispose()
        }
      }
    })
    this.camera.controls.dispose()
    this.renderer.instance.dispose()
  }
  destoryObject(object3D, parent) {
    parent.remove(object3D)
    const children = object3D.children
    if (!children.length) {
      return
    }
    children.forEach(({ geometry, material, childrens }) => {
      geometry.dispose()
      if (Array.isArray(material)) {
        material.forEach((m) => m.dispose())
      } else {
        material?.dispose()
      }
      if (childrens && childrens.length) {
        childrens.forEach((item) => this.destoryObject(item, object3D))
      }
    })
  }
}
